import { tokenKey } from './env'
import Cookies from 'js-cookie'

/**
 *
 * 存储localStore
 * @param {*} name
 * @param {*} content
 */
const setStore = (name, content) => {
  if (!name) return
  if (typeof content !== 'string') {
    content = JSON.stringify(content)
  }
  window.localStorage.setItem(name, content)
}

/**
 *
 * 获取localStore
 * @param {*} name
 */
const getStore = name => {
  if (!name) return
  return window.localStorage.getItem(name)
}

/**
 *
 * 删除localStore
 * @param {*} name
 * @returns
 */
const removeStore = name => {
  if (!name) return
  window.localStorage.removeItem(name)
}

/**
 *
 * 设置token
 * @param {*} token
 */
const setToken = token => {
  return Cookies.set(tokenKey, token)
}

/**
 *
 * 获取token
 * @returns
 */
const getToken = () => {
  return Cookies.get(tokenKey)
}

/**
 *
 * 移除token
 * @returns
 */
const removeToken = () => {
  return Cookies.remove(tokenKey)
}

/**
 *
 * 解码
 * @returns
 */
const b64DecodeUnicode = str => {
  return decodeURIComponent(
    atob(str)
      .split('')
      .map(function(c) {
        return '%' + ('00' + c.charCodeAt(0).toString(16)).slice(-2)
      })
      .join('')
  )
}

/**
 *
 * 加密
 * @returns
 */
const b64EncodeUnicode = str => {
  return btoa(
    encodeURIComponent(str).replace(/%([0-9A-F]{2})/g, function(match, p1) {
      return String.fromCharCode('0x' + p1)
    })
  )
}

/**
 *
 * 获取style样式
 * @param {*} element
 * @param {*} attr
 * @param {string} [NumberMode='int']
 * @returns
 */
const getStyle = (element, attr, NumberMode = 'int') => {
  let target
  // scrollTop 获取方式不同，没有它不属于style，而且只有document.body才能用
  if (attr === 'scrollTop') {
    target = element.scrollTop
  } else if (element.currentStyle) {
    target = element.currentStyle[attr]
  } else {
    target = document.defaultView.getComputedStyle(element, null)[attr]
  }
  //在获取 opactiy 时需要获取小数 parseFloat
  return NumberMode == 'float' ? parseFloat(target) : parseInt(target)
}

/**
 *
 * 上拉加载
 * @param {*} element
 * @param {*} callback
 */
const loadMore = (element, callback) => {
  let windowHeight = window.screen.height
  let height
  let setTop
  let paddingBottom
  let marginBottom
  let requestFram
  let oldScrollTop

  document.body.addEventListener(
    'scroll',
    () => {
      loadMore()
    },
    false
  )
  //运动开始时获取元素 高度 和 offseTop, pading, margin
  element.addEventListener(
    'touchstart',
    () => {
      height = element.offsetHeight
      setTop = element.offsetTop
      paddingBottom = getStyle(element, 'paddingBottom')
      marginBottom = getStyle(element, 'marginBottom')
    },
    { passive: true }
  )

  //运动过程中保持监听 scrollTop 的值判断是否到达底部
  element.addEventListener(
    'touchmove',
    () => {
      loadMore()
    },
    { passive: true }
  )

  //运动结束时判断是否有惯性运动，惯性运动结束判断是非到达底部
  element.addEventListener(
    'touchend',
    () => {
      oldScrollTop = document.body.scrollTop
      moveEnd()
    },
    { passive: true }
  )

  const moveEnd = () => {
    requestFram = requestAnimationFrame(() => {
      if (document.body.scrollTop != oldScrollTop) {
        oldScrollTop = document.body.scrollTop
        loadMore()
        moveEnd()
      } else {
        cancelAnimationFrame(requestFram)
        //为了防止鼠标抬起时已经渲染好数据从而导致重获取数据，应该重新获取dom高度
        height = element.offsetHeight
        loadMore()
      }
    })
  }

  const loadMore = () => {
    if (
      document.body.scrollTop + windowHeight >=
      height + setTop + paddingBottom + marginBottom
    ) {
      callback()
    }
  }
}

/**
 *
 * 显示返回顶部按钮，开始、结束、运动 三个过程中调用函数判断是否达到目标点
 * @param {*} callback
 */
const showBack = callback => {
  let requestFram
  let oldScrollTop

  document.addEventListener(
    'scroll',
    () => {
      showBackFun()
    },
    false
  )
  document.addEventListener(
    'touchstart',
    () => {
      showBackFun()
    },
    { passive: true }
  )

  document.addEventListener(
    'touchmove',
    () => {
      showBackFun()
    },
    { passive: true }
  )

  document.addEventListener(
    'touchend',
    () => {
      oldScrollTop = document.body.scrollTop
      moveEnd()
    },
    { passive: true }
  )

  const moveEnd = () => {
    requestFram = requestAnimationFrame(() => {
      if (document.body.scrollTop != oldScrollTop) {
        oldScrollTop = document.body.scrollTop
        moveEnd()
      } else {
        cancelAnimationFrame(requestFram)
      }
      showBackFun()
    })
  }

  //判断是否达到目标点
  const showBackFun = () => {
    if (document.body.scrollTop > 500) {
      callback(true)
    } else {
      callback(false)
    }
  }
}

/**
 * 运动效果
 * @param {HTMLElement} element   运动对象，必选
 * @param {JSON}        target    属性：目标值，必选
 * @param {number}      duration  运动时间，可选
 * @param {string}      mode      运动模式，可选
 * @param {function}    callback  可选，回调函数，链式动画
 */
const animate = (
  element,
  target,
  duration = 400,
  mode = 'ease-out',
  callback
) => {
  clearInterval(element.timer)

  //判断不同参数的情况
  if (duration instanceof Function) {
    callback = duration
    duration = 400
  } else if (duration instanceof String) {
    mode = duration
    duration = 400
  }

  //判断不同参数的情况
  if (mode instanceof Function) {
    callback = mode
    mode = 'ease-out'
  }

  //获取dom样式
  const attrStyle = attr => {
    if (attr === 'opacity') {
      return Math.round(getStyle(element, attr, 'float') * 100)
    } else {
      return getStyle(element, attr)
    }
  }
  //根字体大小，需要从此将 rem 改成 px 进行运算
  const rootSize = parseFloat(document.documentElement.style.fontSize)

  const unit = {}
  const initState = {}

  //获取目标属性单位和初始样式值
  Object.keys(target).forEach(attr => {
    if (/[^\d^.]+/gi.test(target[attr])) {
      unit[attr] = target[attr].match(/[^\d^.]+/gi)[0] || 'px'
    } else {
      unit[attr] = 'px'
    }
    initState[attr] = attrStyle(attr)
  })

  //去掉传入的后缀单位
  Object.keys(target).forEach(attr => {
    if (unit[attr] == 'rem') {
      target[attr] = Math.ceil(parseInt(target[attr]) * rootSize)
    } else {
      target[attr] = parseInt(target[attr])
    }
  })

  let flag = true //假设所有运动到达终点
  const remberSpeed = {} //记录上一个速度值,在ease-in模式下需要用到
  element.timer = setInterval(() => {
    Object.keys(target).forEach(attr => {
      let iSpeed = 0 //步长
      let status = false //是否仍需运动
      let iCurrent = attrStyle(attr) || 0 //当前元素属性址
      let speedBase = 0 //目标点需要减去的基础值，三种运动状态的值都不同
      let intervalTime //将目标值分为多少步执行，数值越大，步长越小，运动时间越长
      let oldspeed = remberSpeed[attr] || 0
      switch (mode) {
        case 'ease-out':
          speedBase = iCurrent
          intervalTime = (duration * 5) / 400
          break
        case 'linear':
          speedBase = initState[attr]
          intervalTime = (duration * 20) / 400
          break
        case 'ease-in':
          iSpeed = oldspeed + (target[attr] - initState[attr]) / duration
          remberSpeed[attr] = iSpeed
          break
        default:
          speedBase = iCurrent
          intervalTime = (duration * 5) / 400
      }
      if (mode !== 'ease-in') {
        iSpeed = (target[attr] - speedBase) / intervalTime
        iSpeed = iSpeed > 0 ? Math.ceil(iSpeed) : Math.floor(iSpeed)
      }
      //判断是否达步长之内的误差距离，如果到达说明到达目标点
      switch (mode) {
        case 'ease-out':
          status = iCurrent != target[attr]
          break
        case 'linear':
          status =
            Math.abs(Math.abs(iCurrent) - Math.abs(target[attr])) >
            Math.abs(iSpeed)
          break
        case 'ease-in':
          status =
            Math.abs(Math.abs(iCurrent) - Math.abs(target[attr])) >
            Math.abs(iSpeed)
          break
        default:
          status = iCurrent != target[attr]
      }

      if (status) {
        flag = false
        //opacity 和 scrollTop 需要特殊处理
        if (attr === 'opacity') {
          element.style.filter = 'alpha(opacity:' + (iCurrent + iSpeed) + ')'
          element.style.opacity = (iCurrent + iSpeed) / 100
        } else if (attr === 'scrollTop') {
          element.scrollTop = iCurrent + iSpeed
        } else {
          element.style[attr] = iCurrent + iSpeed + 'px'
        }
      } else {
        flag = true
      }

      if (flag) {
        clearInterval(element.timer)
        if (callback) {
          callback()
        }
      }
    })
  }, 20)
}

export {
  setStore,
  getStore,
  removeStore,
  getToken,
  setToken,
  removeToken,
  b64DecodeUnicode,
  b64EncodeUnicode,
  loadMore,
  showBack,
  animate
}
